Ported EU alignment approach for inSchool and Fertility processes#482
Conversation
…d FertilityAlignment: the root-search objective now sums probit probabilities deterministically instead of running a stochastic simulation and counting outcomes, eliminating noise in the intercept search.
There was a problem hiding this comment.
Pull request overview
This PR ports an EU-style “smooth” alignment approach for the in-school and fertility processes, shifting alignment evaluation from stochastic realisations to deterministic expected probabilities and narrowing the in-school adjustment to continuing students (E1a) only.
Changes:
- Updated
Person.inSchool()so the alignment adjustment no longer affects education re-entry (E1b). - Reworked
InSchoolAlignmentto compute a smooth expected student share objective using probit probabilities, applying the adjustment only to E1a. - Reworked
FertilityAlignmentto compute a smooth expected fertility-rate objective using probit probabilities instead of mutating person state.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/main/java/simpaths/model/Person.java | Stops applying the in-school adjustment to the E1b (re-entry) probability. |
| src/main/java/simpaths/model/InSchoolAlignment.java | Replaces stochastic re-simulation with a deterministic expected-share objective; applies adjustment only to E1a. |
| src/main/java/simpaths/model/FertilityAlignment.java | Replaces stochastic re-simulation with a deterministic expected-rate objective based on summed probabilities. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public double evaluate(double[] args) { | ||
|
|
||
| // Ensure each trial point is evaluated from lagged status (pure function for root search). | ||
| persons.parallelStream().forEach(person -> { | ||
| if (person.getLabC4L1() != null) { | ||
| person.setLabC4(person.getLabC4L1()); | ||
| } | ||
| person.inSchool(args[0]); | ||
| }); | ||
| if (numEligible == 0) return targetStudentShare; | ||
|
|
||
| return targetStudentShare - evalStudentShare(); | ||
| // Lagged students within quitting age range: E1a probability of remaining (adjustment applies) | ||
| // Students above MAX_AGE_TO_STAY_IN_CONTINUOUS_EDUCATION deterministically leave → contribute 0 | ||
| double expectedContinuing = persons.parallelStream() | ||
| .filter(person -> person.getLabC4() != null | ||
| && person.getDemAge() >= MIN_STUDENT_AGE | ||
| && person.getDemAge() <= MAX_STUDENT_AGE | ||
| && Les_c4.Student.equals(person.getLabC4L1()) | ||
| && person.getDemAge() >= Parameters.MIN_AGE_TO_LEAVE_EDUCATION | ||
| && person.getDemAge() <= Parameters.MAX_AGE_TO_STAY_IN_CONTINUOUS_EDUCATION) | ||
| .mapToDouble(person -> { | ||
| double score = Parameters.getRegEducationE1a().getScore(person, Person.DoublesVariables.class); | ||
| return Parameters.getRegEducationE1a().getProbability(score + args[0]); | ||
| }) | ||
| .sum(); | ||
|
|
||
| double expectedStudents = numDeterministicStudents + expectedContinuing + baseReEntryExpected; | ||
| double expectedStudentShare = expectedStudents / numEligible; | ||
| return targetStudentShare - expectedStudentShare; |
There was a problem hiding this comment.
Thanks you @dav-sonn,
I have fixed the issue - needed to re-order inSchool alignment in the BuildSchedule.
There was a problem hiding this comment.
@dav-sonn, could you please do re-review with Copilot?
(I can't do it myself without a pro subscription yet..)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
| private long numEligible; | ||
| private long numDeterministicStudents; | ||
| private double baseReEntryExpected; |
| // Lagged students below minimum quitting age: deterministically remain students | ||
| numDeterministicStudents = persons.stream() | ||
| .filter(person -> person.getLabC4() != null | ||
| && person.getDemAge() >= MIN_STUDENT_AGE | ||
| && person.getDemAge() <= MAX_STUDENT_AGE | ||
| && Les_c4.Student.equals(person.getLabC4L1()) | ||
| && person.getDemAge() < Parameters.MIN_AGE_TO_LEAVE_EDUCATION) | ||
| .count(); |
| double expectedStudents = numDeterministicStudents + expectedContinuing + baseReEntryExpected; | ||
| double expectedStudentShare = expectedStudents / numEligible; |
There was a problem hiding this comment.
The last Copilot comments are not critical; rather, they are misleading: they flag that numDeterministicStudents is dead code because its filter (demAge >= 16 && demAge < 16) can never match, but we may change the parameters for 16y.o. in the future, so it is better to have this condition in place.
I will proceed with merging this PR.
There was a problem hiding this comment.
Agree - I hadn't flagged them as well.
No description provided.